{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# FINN - Analysis Passes\n",
    "--------------------------------------\n",
    "This notebook is about analysis passes in FINN. The procedure for creating an analysis pass is shown using an example.\n",
    "\n",
    "We'll use the following utility functions to print the source code for function calls (`showSrc()`) and to visualize a network using netron (`showInNetron()`) in the Jupyter notebook:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from finn.util.visualization import showSrc, showInNetron"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## General Information\n",
    "------------------------------\n",
    "* traverses the graph structure and produces information about certain properties\n",
    "* input: ModelWrapper\n",
    "* returns dictionary of named properties that the analysis extracts"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Example - Quantity analysis of operation types\n",
    "As an example, an analysis is designed that returns the number of nodes of the same operation types."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "First the model is shown to illustrate the analysis. For this netron is used. Netron is a visualizer for neural network, deep learning and machine learning models."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Serving '../LFCW1A1.onnx' at http://0.0.0.0:8081\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "\n",
       "        <iframe\n",
       "            width=\"100%\"\n",
       "            height=\"400\"\n",
       "            src=\"http://0.0.0.0:8081/\"\n",
       "            frameborder=\"0\"\n",
       "            allowfullscreen\n",
       "        ></iframe>\n",
       "        "
      ],
      "text/plain": [
       "<IPython.lib.display.IFrame at 0x7f14142de3c8>"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "showInNetron(\"../LFCW1A1.onnx\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The onnx model has to be converted to a format that can be processed by FINN. This is done with ModelWrapper. As described in the short introduction, this is the format an analysis pass takes as input."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from finn.core.modelwrapper import ModelWrapper\n",
    "model = ModelWrapper('../LFCW1A1.onnx')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The idea is to count all nodes that have the same operation type. The result should contain the operation types and the corresponding number of nodes that occur in the model. In the beginning an empty dictionary is created which is filled by the function and returned as result to the user at the end of the analysis."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def count_equal_nodes(model):\n",
    "    count_dict = {}\n",
    "    for node in model.graph.node:\n",
    "        if node.op_type in count_dict:\n",
    "            count_dict[node.op_type] +=1\n",
    "        else:\n",
    "            count_dict[node.op_type] = 1\n",
    "    return count_dict"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The function takes the model as input and iterates over the nodes. Then it is checked whether there is already an entry for the operation type in the dictionary. If this is not the case, an entry is created and set to `1`. If there is already an entry, it is incremented. If all nodes in the model have been iterated, the filled dictionary is returned."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The analysis function of ModelWrapper is used to perform the analysis just designed. It is shown below and takes the function as input and performs it by passing the model to the function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "    def analysis(self, analysis_fxn):\n",
      "        \"\"\"Runs given anaylsis_fxn on this model and return resulting dict.\"\"\"\n",
      "        return analysis_fxn(self)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "showSrc(ModelWrapper.analysis)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The result can now simply be determined by calling the `.analysis` function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'Shape': 1, 'Gather': 1, 'Unsqueeze': 5, 'Concat': 1, 'Reshape': 1, 'Mul': 5, 'Sub': 1, 'Sign': 4, 'MatMul': 4, 'BatchNormalization': 3, 'Squeeze': 3}\n"
     ]
    }
   ],
   "source": [
    "print(model.analysis(count_equal_nodes))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
